package com.fy.fyspace.service.impl;

import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fy.fyspace.common.result.R;
import com.fy.fyspace.constant.KeysConstant;
import com.fy.fyspace.constant.ResultMessageConstant;
import com.fy.fyspace.constant.SysConstant;
import com.fy.fyspace.ex.handler.ExHandler;
import com.fy.fyspace.mapper.FileInfoMapper;
import com.fy.fyspace.mapper.PictureMapper;
import com.fy.fyspace.mapper.SpaceMapper;
import com.fy.fyspace.mapper.UserMapper;
import com.fy.fyspace.model.dto.user.UserLoginDTO;
import com.fy.fyspace.model.dto.user.UserQueryDTO;
import com.fy.fyspace.model.dto.user.UserRegisterDTO;
import com.fy.fyspace.model.entity.FileInfo;
import com.fy.fyspace.model.entity.Picture;
import com.fy.fyspace.model.entity.Space;
import com.fy.fyspace.model.entity.User;
import com.fy.fyspace.model.enums.file.FileDelFlagEnums;
import com.fy.fyspace.model.enums.file.FileFolderTypeEnums;
import com.fy.fyspace.model.enums.picture.PictureReviewStatusEnum;
import com.fy.fyspace.model.enums.space.SpaceLevelEnum;
import com.fy.fyspace.model.enums.user.AccountForbidStatus;
import com.fy.fyspace.model.enums.user.LoginStatusEnum;
import com.fy.fyspace.model.enums.user.UserRoleEnum;
import com.fy.fyspace.model.enums.user.UserStatusEnum;
import com.fy.fyspace.model.vo.user.*;
import com.fy.fyspace.service.UserService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import static cn.hutool.core.bean.BeanUtil.copyProperties;
import static com.fy.fyspace.common.result.R.ok;
import static com.fy.fyspace.constant.KeysConstant.CHECK_CODE;
import static com.fy.fyspace.constant.KeysConstant.LOGIN_SESSION_KEY;

@Service("userServiceImpl")
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Resource
    private UserMapper userMapper;
    
    @Resource
    private HttpSession httpSession;

    @Autowired
    private SpaceMapper spaceMapper;

    @Resource
    private FileInfoMapper fileInfoMapper;

    @Resource
    private PictureMapper pictureMapper;

    /**
     * 盐值，混淆密码
     */
    private static final String SALT = SysConstant.PASSWORD_SALT;

    /**
     * 注册
     * @param userRegisterDTO
     * @return R
     */
    @Override
    @Transactional
    public long userRegister(UserRegisterDTO userRegisterDTO) {
        String userAccount = userRegisterDTO.getUserAccount();
        String userPassword = userRegisterDTO.getUserPassword();

        // 打日志
        log.info("用户注册，入参：{}", userRegisterDTO);

        String usCode=userRegisterDTO.getCheckCode();
        String code = (String) httpSession.getAttribute(CHECK_CODE);

        // 添加更详细的日志
        log.info("会话ID: {}", httpSession.getId());
        log.info("会话验证码: [{}], 长度: {}", code, code != null ? code.length() : 0);
        log.info("前端验证码: [{}], 长度: {}", usCode, usCode != null ? usCode.length() : 0);

        // 验证码为空检查
        ExHandler.throwIf(StringUtils.isEmpty(usCode), ResultMessageConstant.CAPTCHA_PARAM_ERROR);
        ExHandler.throwIf(code == null, "验证码已过期，请刷新验证码");

        // 统一转换为大写并去除空格后比较
        String normalizedUsCode = usCode.trim().toUpperCase();
        String normalizedCode = code.trim().toUpperCase();

        log.info("标准化后 - 会话验证码: [{}], 前端验证码: [{}]", normalizedCode, normalizedUsCode);
        ExHandler.throwIf(!normalizedUsCode.equals(normalizedCode), ResultMessageConstant.CAPTCHA_ERROR);

        // 1. 校验
        ExHandler.throwIf(StringUtils.isAnyBlank(userAccount, userPassword)
                || userRegisterDTO == null || userAccount.length() < 5 || userAccount.length() > 10
                || userPassword.length() < 8 || userPassword.length() > 16,
                ResultMessageConstant.PARAMS_ERROR);

        // 加 synchronized 防止同账号用户并发注册问题
        synchronized (userAccount.intern()) {
            try {
                // 先检查是否存在已删除的同名账号
                LambdaQueryWrapper<User> existingUserQuery = new LambdaQueryWrapper<>();
                existingUserQuery.eq(User::getUserAccount, userAccount)
                        .eq(User::getIsDelete,UserStatusEnum.IS_NORMAL.getStatus());
                List<User> allUsers = userMapper.selectList(existingUserQuery);

                log.info("检查账号是否存在 - 账号: {}, 查询结果: {}", userAccount, allUsers);
                
                // 如果找到了未删除的同名账号，说明账号已存在
                ExHandler.throwIf(!allUsers.isEmpty(),ResultMessageConstant.ACCOUNT_EXISTS);

                // 2. 加密
                String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
                
                // 3. 插入数据
                User user = new User();
                user.setUserAccount(userAccount);
                user.setUserName("默认用户名");
                user.setUserAvatar("/user/avatar/default_avatar.png");
                user.setUserPassword(encryptPassword);
                user.setIsDelete(UserStatusEnum.IS_NORMAL.getStatus());
                user.setIsForbid(UserStatusEnum.IS_NORMAL.getStatus());
                user.setCreateTime(new Date());
                user.setUpdateTime(new Date());
                
                log.info("开始插入新用户数据：{}", user);
                int addRes = userMapper.insert(user);
                ExHandler.throwIf(addRes != 1, ResultMessageConstant.SYSTEM_ERROR);
                log.info("新用户数据插入成功，用户ID：{}", user.getId());
                
                // 4. 创建用户空间
                Space space = new Space();
                space.setId(user.getId());
                space.setUserId(user.getId());
                space.setSpaceName(SpaceLevelEnum.COMMON.getText());
                space.setSpaceLevel(SpaceLevelEnum.COMMON.getValue());
                space.setMaxSize(SpaceLevelEnum.COMMON.getMaxSize());
                space.setMaxCount(SpaceLevelEnum.COMMON.getMaxCount());
                space.setTotalSize(0L);
                space.setTotalCount(0L);
                space.setIsDelete(0);
                
                log.info("开始创建用户空间：{}", space);
                boolean res = spaceMapper.insertOrUpdate(space);
                ExHandler.throwIf(!res, ResultMessageConstant.SYSTEM_ERROR);
                log.info("用户空间创建成功");
                
                return user.getId();
            } catch (Exception e) {
                log.error("用户注册失败", e);
                throw e;
            }
        }
    }

    /**
     * 用户登录
     *
     * @param userLoginDTO
     * @return
     */
    @Override
    public LoginUserVO userLogin(UserLoginDTO userLoginDTO) {
        // 打日志
        log.info("用户登录，入参：{}", userLoginDTO);

        // 1. 校验验证码
        String usCode = userLoginDTO.getCheckCode();
        String code = (String) httpSession.getAttribute(CHECK_CODE);

        // 添加更详细的日志
        log.info("会话ID: {}", httpSession.getId());
        log.info("会话验证码: [{}], 长度: {}", code, code != null ? code.length() : 0);
        log.info("前端验证码: [{}], 长度: {}", usCode, usCode != null ? usCode.length() : 0);

        // 验证码为空检查
//        ExHandler.throwIf(StringUtils.isEmpty(usCode), ResultMessageConstant.CAPTCHA_PARAM_ERROR);
//        ExHandler.throwIf(code == null, "验证码已过期，请刷新验证码");
//
//        // 统一转换为大写并去除空格后比较
//        String normalizedUsCode = usCode.trim().toUpperCase();
//        String normalizedCode = code.trim().toUpperCase();
//
//        log.info("标准化后 - 会话验证码: [{}], 前端验证码: [{}]", normalizedCode, normalizedUsCode);
//        ExHandler.throwIf(!normalizedUsCode.equals(normalizedCode), ResultMessageConstant.CAPTCHA_ERROR);

        // 2. 校验登录参数
        String userAccount = userLoginDTO.getUserAccount();
        String userPassword = userLoginDTO.getUserPassword();
        ExHandler.throwIf(StringUtils.isAnyBlank(userAccount, userPassword)
                        || userLoginDTO == null || userAccount.length() < 5 || userAccount.length() > 10
                        || userPassword.length() < 8 || userPassword.length() > 25,
                ResultMessageConstant.PARAMS_ERROR);

        // 3. 加密密码
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());

        // 4. 查询用户
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUserAccount, userAccount)
                .eq(User::getIsDelete,UserStatusEnum.IS_NORMAL.getStatus());
        User user = getOne(queryWrapper);

        // 5. 用户不存在判断
        ExHandler.throwIf(user == null, ResultMessageConstant.USER_NOT_FOUND);

        // 6. 密码校验
        ExHandler.throwIf(!user.getUserPassword().equals(encryptPassword),
                ResultMessageConstant.PASSWORD_ERROR);

        // 7. 用户状态校验
        ExHandler.throwIf(user.getIsForbid() != null && user.getIsForbid() == 1,
                ResultMessageConstant.USER_BANNED);

        // 8. 更新用户登录状态
        user.setLoginStatus(LoginStatusEnum.ONLINE.getCode());
        saveOrUpdate(user);

        // 9. 创建登录返回对象
        LoginUserVO loginUserVO = new LoginUserVO();
        copyProperties(user, loginUserVO);

        // 10. 创建会话信息
        UserSessionVO userSessionVO = new UserSessionVO();
        copyProperties(user, userSessionVO);

        // 11. 保存登录会话
        httpSession.setAttribute(LOGIN_SESSION_KEY, userSessionVO);
        httpSession.setMaxInactiveInterval(50000000);

        return loginUserVO;
    }

    /**
     * 获取当前登录用户
     *
     * @return
     */
    @Override
    public LoginUserVO getLoginUser() {
        UserSessionVO userSessionVO = (UserSessionVO) httpSession.getAttribute(KeysConstant.LOGIN_SESSION_KEY);
        ExHandler.throwIf(userSessionVO == null, ResultMessageConstant.NOT_LOGIN_ERROR);

        // 从数据库查询（追求性能的话可以注释，直接走缓存）
        Long userId = userSessionVO.getId();
        User user = this.getById(userId);
        ExHandler.throwIf(user.getIsForbid() != null && user.getIsForbid().equals(AccountForbidStatus.FORBIDDEN.getCode()),
                ResultMessageConstant.USER_BANNED);// 账户异常
        LoginUserVO loginUserVO = new LoginUserVO();
        copyProperties(user, loginUserVO);

        return loginUserVO;
    }

    /**
     * 修改密码
     *
     * @param oldPassword 旧密码
     * @param newPassword 新密码
      会话信息
     * @return
     */
    @Override
    public boolean updatePassword(String oldPassword, String newPassword) {
        // 1. 校验参数
        ExHandler.throwIf(StringUtils.isAnyBlank(oldPassword, newPassword),
                ResultMessageConstant.PARAMS_ERROR);
        ExHandler.throwIf(newPassword.length() < 8 || newPassword.length() > 25,
                ResultMessageConstant.PARAMS_ERROR);

        // 2. 获取当前登录用户
        UserSessionVO userSessionVO = (UserSessionVO) httpSession.getAttribute(KeysConstant.LOGIN_SESSION_KEY);
        ExHandler.throwIf(userSessionVO == null, ResultMessageConstant.NOT_LOGIN_ERROR);

        // 3. 校验旧密码
        String encryptOldPassword = DigestUtils.md5DigestAsHex((SALT + oldPassword).getBytes());
        User user = this.getById(userSessionVO.getId());
        ExHandler.throwIf(!user.getUserPassword().equals(encryptOldPassword),
                ResultMessageConstant.PASSWORD_ERROR);

        // 4. 更新密码
        String encryptNewPassword = DigestUtils.md5DigestAsHex((SALT + newPassword).getBytes());
        User updateUser = new User();
        updateUser.setId(user.getId());
        updateUser.setUserPassword(encryptNewPassword);
        return this.updateById(updateUser);
    }

    /**
     * 修改名称
     *
     * @param userName    新的用户名称
     * @return
     */
    @Override
    public boolean updateUserName(String userName) {
        // 1. 校验参数
        ExHandler.throwIf(StringUtils.isBlank(userName), ResultMessageConstant.PARAMS_ERROR);
        ExHandler.throwIf(userName.length() > 20, ResultMessageConstant.PARAMS_ERROR);

        // 2. 获取当前登录用户
        UserSessionVO userSessionVO = (UserSessionVO) httpSession.getAttribute(KeysConstant.LOGIN_SESSION_KEY);
        ExHandler.throwIf(userSessionVO == null, ResultMessageConstant.NOT_LOGIN_ERROR);

        // 3. 更新用户名
        User updateUser = new User();
        updateUser.setId(userSessionVO.getId());
        updateUser.setUserName(userName);
        return this.updateById(updateUser);
    }

    /**
     * 更新用户简介
     *
     * @param userProfile 新的用户简介
     * @return
     */
    @Override
    public boolean updateUserProfile(String userProfile) {
        // 1. 校验参数
        ExHandler.throwIf(StringUtils.isBlank(userProfile), ResultMessageConstant.PARAMS_ERROR);
        ExHandler.throwIf(userProfile.length() > 200, ResultMessageConstant.PARAMS_ERROR);

        // 2. 获取当前登录用户
        UserSessionVO userSessionVO = (UserSessionVO) httpSession.getAttribute(KeysConstant.LOGIN_SESSION_KEY);
        ExHandler.throwIf(userSessionVO == null, ResultMessageConstant.NOT_LOGIN_ERROR);

        // 3. 更新用户简介
        User updateUser = new User();
        updateUser.setId(userSessionVO.getId());
        updateUser.setUserProfile(userProfile);
        return this.updateById(updateUser);
    }

    /**
     * 获取用户列表
     *
     * @param userQueryDTO
     * @return R
     */
    @Override
    public R getUserList(UserQueryDTO userQueryDTO) {
        log.info("开始获取用户列表，页码：{}，每页数量：{}", userQueryDTO.getPageNo(), userQueryDTO.getPageSize());

        // 设置分页参数
        PageHelper.startPage(userQueryDTO.getPageNo(), userQueryDTO.getPageSize());

        // 构建查询条件
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        if (userQueryDTO.getUserAccount() != null) {
            wrapper.like(User::getUserAccount, userQueryDTO.getUserAccount());
        }
        if (userQueryDTO.getUserName() != null) {
            wrapper.like(User::getUserName, userQueryDTO.getUserName());
        }
        if (userQueryDTO.getIsForbid() != null) {
            wrapper.eq(User::getIsForbid, userQueryDTO.getIsForbid());
        }
        if (userQueryDTO.getLoginStatus() != null) {
            wrapper.eq(User::getLoginStatus, userQueryDTO.getLoginStatus());
        }
        wrapper.eq(User::getIsDelete, UserStatusEnum.IS_NORMAL.getStatus());

        // 执行查询
        List<User> userList = userMapper.selectList(wrapper);

        // 获取分页信息
        PageInfo<User> pageInfo = new PageInfo<>(userList);
        log.info("分页查询结果 - 当前页：{}，每页数量：{}，总记录数：{}，总页数：{}",
                pageInfo.getPageNum(), pageInfo.getPageSize(),
                pageInfo.getTotal(), pageInfo.getPages());

        // 转换为VO并处理id类型转换(Long转String)
        List<UserQueryVO> userVoList = userList.stream().map(user -> {
            UserQueryVO vo = new UserQueryVO();
            copyProperties(user, vo);
            // 处理id从Long到String的转换
            if (user.getId() != null) {
                vo.setId(user.getId().toString());
            }
            return vo;
        }).collect(Collectors.toList());

        // 构建返回结果
        return ok(userVoList);
    }

    /**
     * 获取用户统计信息
     * @return R
     */
    @Override
    public UserStatisticsVO getUserStatistics() {
        // 1. 获取当前登录用户
        UserSessionVO userSessionVO = (UserSessionVO) httpSession.getAttribute(KeysConstant.LOGIN_SESSION_KEY);
        ExHandler.throwIf(userSessionVO == null, ResultMessageConstant.NOT_LOGIN_ERROR);
        Long userId = userSessionVO.getId();

        UserStatisticsVO statistics = new UserStatisticsVO();

        // 2. 获取图片统计信息
        UserStatisticsVO.PictureStatistics pictureStats = new UserStatisticsVO.PictureStatistics();
        
        // 2.1 查询用户的所有未删除图片
        LambdaQueryWrapper<Picture> pictureQuery = new LambdaQueryWrapper<>();
        pictureQuery.eq(Picture::getUserId, userId)
                   .eq(Picture::getIsDelete, 0);
        List<Picture> userPictures = pictureMapper.selectList(pictureQuery);

        // 2.2 计算基本统计信息
        pictureStats.setTotalCount((long) userPictures.size());
        pictureStats.setTotalSize(userPictures.stream()
                .mapToLong(Picture::getFileSize)
                .sum());

        // 2.3 计算格式分布
        Map<String, Long> formatDistribution = userPictures.stream()
                .collect(Collectors.groupingBy(
                        Picture::getPicFormat,
                        Collectors.counting()
                ));
        pictureStats.setFormatDistribution(formatDistribution);

        // 2.4 计算分类分布
        Map<String, Long> categoryDistribution = userPictures.stream()
                .filter(p -> p.getCategory() != null)
                .collect(Collectors.groupingBy(
                        Picture::getCategory,
                        Collectors.counting()
                ));
        pictureStats.setCategoryDistribution(categoryDistribution);

        // 2.5 计算标签使用频率
        Map<String, Long> tagFrequency = new HashMap<>();
        userPictures.stream()
                .filter(p -> p.getTags() != null)
                .forEach(p -> {
                    List<String> tags = JSONUtil.toList(p.getTags(), String.class);
                    if (tags != null) {
                        tags.forEach(tag -> 
                            tagFrequency.merge(tag, 1L, Long::sum)
                        );
                    }
                });
        pictureStats.setPopularTags(tagFrequency);

        // 2.6 计算审核状态统计
        pictureStats.setReviewingCount(userPictures.stream()
                .filter(p -> p.getReviewStatus() == PictureReviewStatusEnum.REVIEWING.getValue())
                .count());
        pictureStats.setApprovedCount(userPictures.stream()
                .filter(p -> p.getReviewStatus() == PictureReviewStatusEnum.PASS.getValue())
                .count());
        pictureStats.setRejectedCount(userPictures.stream()
                .filter(p -> p.getReviewStatus() == PictureReviewStatusEnum.REJECT.getValue())
                .count());

        // 3. 获取空间统计信息
        UserStatisticsVO.SpaceStatistics spaceStats = new UserStatisticsVO.SpaceStatistics();
        Space userSpace = spaceMapper.selectById(userId);
        if (userSpace != null) {
            spaceStats.setSpaceLevel(SpaceLevelEnum.getEnumByValue(userSpace.getSpaceLevel()).getText());
            spaceStats.setCreateTime(userSpace.getCreateTime());
            spaceStats.setMaxSize(userSpace.getMaxSize());
            spaceStats.setUsedSize(userSpace.getTotalSize());
            spaceStats.setRemainingSize(userSpace.getMaxSize() - userSpace.getTotalSize());
            spaceStats.setMaxCount(userSpace.getMaxCount());
            spaceStats.setRemainingCount(userSpace.getMaxCount() - userSpace.getTotalCount());
            spaceStats.setUsageRate(userSpace.getMaxSize() > 0 
                ? (double) userSpace.getTotalSize() / userSpace.getMaxSize() * 100 
                : 0.0);
        }

        // 4. 获取活动统计信息
        UserStatisticsVO.ActivityStatistics activityStats = new UserStatisticsVO.ActivityStatistics();

        // 4.1 最近上传的图片
        List<UserStatisticsVO.RecentPicture> recentPictures = userPictures.stream()
                .sorted(Comparator.comparing(Picture::getCreateTime).reversed())
                .limit(9)
                .map(p -> {
                    UserStatisticsVO.RecentPicture rp = new UserStatisticsVO.RecentPicture();
                    rp.setId(p.getId());
                    rp.setName(p.getName());
                    rp.setUrl(p.getUrl());
                    rp.setThumbnailUrl(p.getThumbnailUrl());
                    rp.setUploadTime(p.getCreateTime());
                    return rp;
                })
                .collect(Collectors.toList());
        activityStats.setRecentPictures(recentPictures);

        // 4.2 最常使用的分类
        List<UserStatisticsVO.CategoryUsage> topCategories = categoryDistribution.entrySet().stream()
                .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
                .limit(5)
                .map(entry -> {
                    UserStatisticsVO.CategoryUsage cu = new UserStatisticsVO.CategoryUsage();
                    cu.setCategory(entry.getKey());
                    cu.setCount(entry.getValue());
                    return cu;
                })
                .collect(Collectors.toList());
        activityStats.setTopCategories(topCategories);

        // 4.3 最常使用的标签
        List<UserStatisticsVO.TagUsage> topTags = tagFrequency.entrySet().stream()
                .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
                .limit(10)
                .map(entry -> {
                    UserStatisticsVO.TagUsage tu = new UserStatisticsVO.TagUsage();
                    tu.setTag(entry.getKey());
                    tu.setCount(entry.getValue());
                    return tu;
                })
                .collect(Collectors.toList());
        activityStats.setTopTags(topTags);

        // 5. 设置统计结果
        statistics.setPictureStatistics(pictureStats);
        statistics.setSpaceStatistics(spaceStats);
        statistics.setActivityStatistics(activityStats);

        // 添加文件类型统计
        UserStatisticsVO.FileTypeStatistics fileTypeStats = new UserStatisticsVO.FileTypeStatistics();
        
        // 查询用户的所有未删除文件
        LambdaQueryWrapper<FileInfo> fileQuery = new LambdaQueryWrapper<>();
        fileQuery.eq(FileInfo::getUserId, userId)
                .eq(FileInfo::getDelFlag, FileDelFlagEnums.USING.getFlag())
                .eq(FileInfo::getFolderType, FileFolderTypeEnums.FILE.getType());
        List<FileInfo> userFiles = fileInfoMapper.selectList(fileQuery);

        // 计算文件类型分布
        Map<String, Long> typeDistribution = userFiles.stream()
                .filter(file -> file.getFileType() != null)
                .collect(Collectors.groupingBy(
                        file -> String.valueOf(file.getFileType()),  // 转换为String
                        Collectors.counting()
                ));

        // 只获取typeDistribution是前6组数据
        typeDistribution = typeDistribution.entrySet().stream()
                .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
                .limit(6)
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

        fileTypeStats.setTypeDistribution(typeDistribution);
        fileTypeStats.setTotalFiles((long) userFiles.size());

        // 设置文件类型统计结果
        statistics.setFileTypeStatistics(fileTypeStats);

        return statistics;
    }

    /**
     * 重置密码
     * @param email 邮箱
     * @param password 新密码
     * @param confirmPassword 确认密码
     * @param emailCode 邮箱验证码
     * @return R
     */
    @Override
    public boolean resetPassword(String email, String password, String confirmPassword, String emailCode) {
        // 1. 校验参数
        ExHandler.throwIf(StringUtils.isAnyBlank(email, password, confirmPassword, emailCode),
                ResultMessageConstant.PARAMS_ERROR);
        
        // 2. 校验密码长度
        ExHandler.throwIf(password.length() < 8 || password.length() > 25,
                "密码长度应在8-25位之间");

        // 3. 校验两次密码是否一致
        ExHandler.throwIf(!password.equals(confirmPassword),
                "两次输入的密码不一致");

        // 4. 校验邮箱验证码
        String savedEmailCode = (String) httpSession.getAttribute(KeysConstant.CHECK_CODE_EMAIL);
        ExHandler.throwIf(savedEmailCode == null, ResultMessageConstant.EMAIL_CODE_EXPIRED);
        ExHandler.throwIf(!emailCode.equals(savedEmailCode), ResultMessageConstant.EMAIL_CODE_ERROR);

        // 5. 查询用户是否存在
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getEmail, email)
                .eq(User::getIsForbid,UserStatusEnum.IS_NORMAL.getStatus())
                .eq(User::getIsDelete,UserStatusEnum.IS_NORMAL.getStatus());
        List<User> userList = userMapper.selectList(queryWrapper);
        ExHandler.throwIf(userList == null || userList.isEmpty(), ResultMessageConstant.NOT_BIND_EMAIL);

        // 6. 加密新密码
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + password).getBytes());

        // 7. 更新密码
        User user = userList.get(0);
        User updateUser = new User();
        updateUser.setId(user.getId());
        updateUser.setUserPassword(encryptPassword);
        // 使用java.sql.Date
        updateUser.setUpdateTime(new Date());

        // 8. 清除验证码
        httpSession.removeAttribute(KeysConstant.CHECK_CODE_EMAIL);
        userMapper.updateById(updateUser);

        return true;
    }

    /**
     * 获取用户邮箱
     * @return R
     */
    @Override
    public R getUserEmail() {
        UserSessionVO userSessionVO=(UserSessionVO) httpSession.getAttribute(LOGIN_SESSION_KEY);
        ExHandler.throwIf(userSessionVO==null,ResultMessageConstant.NOT_LOGIN);
        User user = userMapper.selectById(userSessionVO.getId());
        return ok(user.getEmail());
    }

    /**
     * 修改用户邮箱
     * @param email
     * @return R
     */
    @Override
    public R updateUserEmail(String email) {
        UserSessionVO userSessionVO=(UserSessionVO) httpSession.getAttribute(LOGIN_SESSION_KEY);
        ExHandler.throwIf(userSessionVO==null,ResultMessageConstant.NOT_LOGIN);

        User user = userMapper.selectById(userSessionVO.getId());
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getEmail, email)
                .eq(User::getIsForbid, UserStatusEnum.IS_NORMAL.getStatus())
                .eq(User::getIsDelete, UserStatusEnum.IS_NORMAL.getStatus());

        ExHandler.throwIf(user.getEmail()!=null && email.equals(user.getEmail()),"你已经绑定了该邮箱");
        ExHandler.throwIf(userMapper.selectCount(queryWrapper)>0,ResultMessageConstant.EMAIL_EXIST);

        // 新增邮箱格式校验
        ExHandler.throwIf(!email.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$"), "请输入正确格式的邮箱!");

        user.setEmail(email);
        this.updateById(user);
        return ok();
    }

    /**
     * 导出用户列表
     * @param isForbid 状态（0-正常，1-禁用）
     * @param month 月份（yyyy-MM）
     * @param response HttpServletResponse
     */
    @Override
    public void exportUserExcel(Integer isForbid, String month, HttpServletResponse response) {
        try {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(User::getIsDelete, 0);
            String forbidStr = "全部";
            if (isForbid != null) {
                wrapper.eq(User::getIsForbid, isForbid);
                forbidStr = (isForbid == 1) ? "禁用" : (isForbid == 0 ? "正常" : ("状态" + isForbid));
            }
            String monthStr = "";
            if (month != null && !month.trim().isEmpty()) {
                try {
                    LocalDate startDate = LocalDate.parse(month + "-01", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                    LocalDateTime start = startDate.atStartOfDay();
                    LocalDateTime end = start.plusMonths(1);
                    wrapper.ge(User::getCreateTime, start);
                    wrapper.lt(User::getCreateTime, end);
                    monthStr = month;
                } catch (Exception e) {
                    throw new RuntimeException("月份格式错误，应为yyyy-MM");
                }
            }
            List<User> userList = userMapper.selectList(wrapper);
            List<UserExcelPortVO> voList = userList.stream().map(u -> {
                UserExcelPortVO vo = new UserExcelPortVO();
                vo.setUserAccount(u.getUserAccount());
                vo.setUserName(u.getUserName());
                vo.setUserProfile(u.getUserProfile());
                // 角色中文
                vo.setUserRole(UserRoleEnum.getEnumByType(u.getUserRole()).getDesc());
                // 时间转字符串
                vo.setCreateTime(u.getCreateTime() != null ? formatDate(u.getCreateTime()) : "");
                vo.setEditTime(u.getEditTime() != null ? formatDate(u.getEditTime()) : "");
                vo.setUpdateTime(u.getUpdateTime() != null ? formatDate(u.getUpdateTime()) : "");
                vo.setEmail(u.getEmail());
                // 状态中文
                vo.setIsForbid(UserStatusEnum.getEnumByStatus(u.getIsForbid()).getDesc());
                vo.setIsDelete(u.getIsDelete() != null && u.getIsDelete() == 1 ? "已删除" : "未删除");
                return vo;
            }).collect(Collectors.toList());
            // 文件名
            StringBuilder fileNameBuilder = new StringBuilder("用户信息表");
            if (!"全部".equals(forbidStr)) {
                fileNameBuilder.append("-").append(forbidStr);
            }
            if (!"".equals(monthStr)) {
                fileNameBuilder.append("-").append(monthStr);
            }
            fileNameBuilder.append(".xlsx");
            String fileName = URLEncoder.encode(fileNameBuilder.toString(), "UTF-8");
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-disposition", "attachment;filename=" + fileName);
            EasyExcel.write(response.getOutputStream(), UserExcelPortVO.class)
                    .sheet("用户信息")
                    .doWrite(voList);
        } catch (Exception e) {
            log.error("导出用户信息表失败", e);
            throw new RuntimeException("导出失败: " + e.getMessage());
        }
    }

    // 工具方法：格式化时间
    private String formatDate(Date date) {
        return new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
    }

    @Override
    public int testU(){
        return 1;
    }
}
